﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace MartinCarolan.EventSpy.UI
{
    /// <summary>
    /// Interaction logic for ObjectExplorer.xaml
    /// </summary>
    public partial class ObjectExplorer : UserControl
    {
        public ObjectExplorer()
        {
            DataContext = this;
            InitializeComponent();
        }

        public object Instance
        {
            get { return (object)GetValue(InstanceProperty); }
            set { SetValue(InstanceProperty, value); }
        }

        // Using a DependencyProperty as the backing store for Instance.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty InstanceProperty =
            DependencyProperty.Register("Instance", typeof(object), typeof(ObjectExplorer), new UIPropertyMetadata(null, InstanceChanged));

        private Stack<object> navigationHistory = new Stack<object>();

        private static void InstanceChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            var that = sender as ObjectExplorer;
            that.Expand(that.Instance);
        }

        private void InvokeOnCorrectDispatcher(object o, Delegate d)
        {
            if (o is UIElement)
            {
                ((UIElement)o).Dispatcher.Invoke(d);
            }
            else if (o is System.Windows.Forms.Control)
            {
                ((System.Windows.Forms.Control)o).BeginInvoke(d);
            }
            else
            {
                Dispatcher.Invoke(d, null);
            }
        }

        private void Expand(object o)
        {
            if (o == null)
            {
                return;
            }

            navigationHistory.Push(o);
            var properties = new List<Property>();

            //add property and field items.
            //if we have met a UIElement be sure to do this on its associated dispatcher
            foreach (var property in o.GetType().GetFields())
            {
                InvokeOnCorrectDispatcher(o, new Action(
                    delegate
                    {
                        properties.Add(new Property() { Name = property.Name, Value = property.GetValue(o), IsField = true, FieldType = property.FieldType });
                    }));
            }

            foreach (var property in o.GetType().GetProperties())
            {
                InvokeOnCorrectDispatcher(o, new Action(
                    delegate
                    {
                        properties.Add(new Property() { Name = property.Name, Value = property.GetValue(o, null), IsField = false, FieldType = property.PropertyType });
                    }));
            }

            _grid.ItemsSource = properties;
        }

        private void MouseUp(object sender, MouseButtonEventArgs e)
        {
            if (_grid.SelectedItem == null)
            {
                return;
            }

            var property = _grid.SelectedItem as Property;
            Expand(property.Value);
        }

        private void MouseRightButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (navigationHistory.Count == 0)
            {
                return;
            }

            navigationHistory.Pop();

            if (navigationHistory.Count > 0)
            {
                Expand(navigationHistory.Pop());
            }
        }
    }

    public class Property
    {
        public string Name { get; set; }
        public object Value { get; set; }
        public Type FieldType { get; set; }
        public bool IsField { get; set; }
    }
}
